<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>LalServer SDK 测试页面 - FLV播放器</title>
    <style>
        /* 样式保持不变 */
        body {
            font-family: Arial, sans-serif;
            max-width: 1000px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        .container {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 4px rgba(0,0,0,0.1);
        }
        .section {
            margin-bottom: 30px;
            padding: 15px;
            border: 1px solid #ddd;
            border-radius: 5px;
        }
        .section h3 {
            margin-top: 0;
            color: #333;
        }
        input, button, select {
            padding: 8px 12px;
            margin: 5px;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background-color: #007bff;
            color: white;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
        .result {
            margin-top: 10px;
            padding: 10px;
            background-color: #f8f9fa;
            border-radius: 4px;
            border-left: 4px solid #007bff;
            overflow-x: auto;
        }
        .error {
            border-left-color: #dc3545;
            background-color: #f8d7da;
        }
        .success {
            border-left-color: #28a745;
            background-color: #d4edda;
        }
        .player-section {
            margin-top: 20px;
        }
        .url-display {
            background-color: #e9ecef;
            padding: 10px;
            border-radius: 4px;
            margin: 5px 0;
            word-break: break-all;
            font-family: monospace;
            font-size: 12px;
        }
        .copy-btn {
            background-color: #6c757d;
            font-size: 12px;
            padding: 4px 8px;
        }
        .copy-btn:hover {
            background-color: #5a6268;
        }
        .token-input {
            width: 300px;
            margin-bottom: 10px;
        }
        .player-placeholder {
            display: flex;
            align-items: center;
            justify-content: center;
            height: 300px;
            background-color: #f0f0f0;
            border: 2px dashed #ccc;
            color: #666;
            font-size: 16px;
            text-align: center;
        }
        .player-controls {
            margin-top: 10px;
            display: flex;
            gap: 10px;
        }
        .loading {
            border: 3px solid #f3f3f3;
            border-top: 3px solid #007bff;
            border-radius: 50%;
            width: 20px;
            height: 20px;
            animation: spin 1s linear infinite;
            display: inline-block;
            margin-right: 10px;
        }
        .stats-display {
            margin-top: 10px;
            padding: 8px;
            background-color: #f0f8ff;
            border-radius: 4px;
            font-size: 12px;
            font-family: monospace;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        .flv-badge {
            background-color: #ff6b6b;
            color: white;
            padding: 3px 8px;
            border-radius: 4px;
            font-size: 12px;
            margin-left: 10px;
        }
        .optimization-controls {
            background-color: #f8f9fa;
            padding: 10px;
            border-radius: 4px;
            margin-top: 10px;
            border-left: 4px solid #17a2b8;
        }
        .optimization-controls h4 {
            margin-top: 0;
            color: #17a2b8;
        }
    </style>
    <!-- 引入 flv.js 用于FLV播放 -->
    <script src="https://cdn.jsdelivr.net/npm/flv.js@1.6.2/dist/flv.min.js"></script>
</head>
<body>
<div class="container">
    <h1>LalServer SDK 测试页面 <span class="flv-badge">FLV格式</span></h1>

    <div class="section">
        <h3>启动拉流</h3>
        <input type="text" id="streamName" placeholder="流名称 (如: cam01)" value="cam01">
        <input type="text" id="rtspUrl" placeholder="RTSP地址" value="rtsp://user:pass@192.168.1.100:554/stream1" style="width: 400px;">
        <br>
        <button onclick="startPull()">启动拉流</button>
        <div id="startPullResult"></div>
    </div>

    <div class="section">
        <h3>停止拉流</h3>
        <input type="text" id="stopStreamName" placeholder="流名称 (如: cam01)" value="cam01">
        <button onclick="stopPull()">停止拉流</button>
        <div id="stopPullResult"></div>
    </div>

    <div class="section">
        <h3>获取FLV播放地址</h3>
        <input type="text" id="urlStreamName" placeholder="流名称 (如: cam01)" value="cam01">
        <br>
        <button onclick="getUrls()">获取FLV地址</button>
        <div id="urlsResult"></div>
    </div>

    <div class="section">
        <h3>流状态查询</h3>
        <input type="text" id="statusStreamName" placeholder="流名称 (如: cam01)" value="cam01">
        <button onclick="getStatus()">查询状态</button>
        <div id="statusResult"></div>
    </div>

    <div class="section">
        <h3>FLV播放器测试</h3>
        <div>
            <button onclick="setupPlayer()">开始播放</button>
            <button onclick="stopPlayer()">停止播放</button>
        </div>

        <div class="optimization-controls">
            <h4>流畅度优化选项</h4>
            <label>
                <input type="checkbox" id="enableStashBuffer" checked> 启用缓冲 (推荐)
            </label>
            <label>
                <input type="range" id="stashSize" min="128" max="1024" step="128" value="384"> 缓冲区大小: <span id="stashSizeValue">384</span>KB
            </label>
            <label>
                <input type="checkbox" id="enableLowLatency" checked> 低延迟模式
            </label>
            <label>
                <input type="checkbox" id="autoReconnect" checked> 自动重新连接
            </label>
        </div>

        <div id="playerContainer" class="player-section">
            <div id="playerPlaceholder" class="player-placeholder">
                请点击"开始播放"按钮初始化FLV播放器
            </div>
            <video id="videoPlayer" controls width="100%" height="300" style="display: none;">
                您的浏览器不支持视频播放
            </video>
            <div id="playerStatus" class="result"></div>
            <div id="playerStats" class="stats-display" style="display: none;"></div>
            <div class="player-controls" style="display: none;" id="playerControls">
                <button onclick="togglePlay()">播放/暂停</button>
                <button onclick="toggleMute()">静音/取消静音</button>
                <button onclick="toggleFullscreen()">全屏</button>
                <button onclick="reconnectPlayer()">重新连接</button>
            </div>
        </div>
    </div>
</div>

<script>
    const API_BASE = '/api/lalserver';
    let flvPlayer = null;
    let isPlayerInitialized = false;
    let playbackMonitor = null;
    let currentPlayUrl = '';
    let reconnectAttempts = 0;
    const MAX_RECONNECT_ATTEMPTS = 5;

    // 优化配置
    const optimizationConfig = {
        enableStashBuffer: true,
        stashInitialSize: 384, // 增大初始缓冲区
        enableLowLatency: true,
        autoReconnect: true,
        lazyLoad: false,
        accurateSeek: false
    };

    // 初始化优化选项
    document.getElementById('enableStashBuffer').addEventListener('change', function(e) {
        optimizationConfig.enableStashBuffer = e.target.checked;
    });

    document.getElementById('stashSize').addEventListener('input', function(e) {
        optimizationConfig.stashInitialSize = parseInt(e.target.value);
        document.getElementById('stashSizeValue').textContent = e.target.value;
    });

    document.getElementById('enableLowLatency').addEventListener('change', function(e) {
        optimizationConfig.enableLowLatency = e.target.checked;
    });

    document.getElementById('autoReconnect').addEventListener('change', function(e) {
        optimizationConfig.autoReconnect = e.target.checked;
    });

    // API调用函数保持不变
    async function startPull() {
        const streamName = document.getElementById('streamName').value;
        const rtspUrl = document.getElementById('rtspUrl').value;

        try {
            const response = await fetch(`${API_BASE}/start-pull`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ streamName, rtspUrl })
            });
            const result = await response.json();
            showResult('startPullResult', result, result.success);
        } catch (error) {
            showResult('startPullResult', { error: error.message }, false);
        }
    }

    async function stopPull() {
        const streamName = document.getElementById('stopStreamName').value;
        const rtspUrl = document.getElementById('rtspUrl').value;
        try {
            const response = await fetch(`${API_BASE}/stop-pull`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ streamName, rtspUrl })
            });
            const result = await response.json();
            showResult('stopPullResult', result, result.success);
        } catch (error) {
            showResult('stopPullResult', { error: error.message }, false);
        }
    }

    async function getUrls() {
        const streamName = document.getElementById('urlStreamName').value;

        try {
            const url = `${API_BASE}/urls/${streamName}`;
            const response = await fetch(url);
            const result = await response.json();

            if (result.success && result.data) {
                const urls = result.data;
                let html = '<div class="success"><strong>FLV播放地址：</strong><br>';

                if (urls.flv) {
                    html += `<div class="url-display">
                        <strong>FLV:</strong> ${urls.flv}
                        <button class="copy-btn" onclick="copyToClipboard('${urls.flv}')">复制</button>
                    </div>`;
                } else {
                    html += '<div class="error">未找到FLV播放地址</div>';
                }

                html += '</div>';
                document.getElementById('urlsResult').innerHTML = html;
            } else {
                showResult('urlsResult', result, false);
            }
        } catch (error) {
            showResult('urlsResult', { error: error.message }, false);
        }
    }

    async function getStatus() {
        const streamName = document.getElementById('statusStreamName').value;

        try {
            const response = await fetch(`${API_BASE}/status/${streamName}`);
            const result = await response.json();
            showResult('statusResult', result, result.success);
        } catch (error) {
            showResult('statusResult', { error: error.message }, false);
        }
    }

    function setupPlayer() {
        const streamName = document.getElementById('urlStreamName').value || 'cam01';

        stopPlayer(); // 先停止当前播放
        reconnectAttempts = 0; // 重置重连尝试次数

        // 显示加载状态
        document.getElementById('playerStatus').innerHTML =
            '<div class="result"><span class="loading"></span>正在获取FLV播放地址...</div>';

        // 获取播放地址并初始化播放器
        getPlayUrl(streamName);
    }

    async function getPlayUrl(streamName) {
        try {
            const url = `${API_BASE}/urls/${streamName}`;
            const response = await fetch(url);
            const result = await response.json();

            if (result.success && result.data) {
                const urls = result.data;
                let playUrl = urls.flv;
                if (playUrl) {
                    document.getElementById('playerPlaceholder').style.display = 'none';
                    document.getElementById('videoPlayer').style.display = 'block';
                    document.getElementById('playerControls').style.display = 'flex';
                    document.getElementById('playerStats').style.display = 'block';

                    document.getElementById('playerStatus').innerHTML = '<div class="result">正在初始化FLV播放器...</div>';
                    isPlayerInitialized = true;
                    currentPlayUrl = playUrl;

                    setupFlvPlayer(playUrl);
                    startPlaybackMonitor();
                } else {
                    document.getElementById('playerStatus').innerHTML =
                        `<div class="error">未找到FLV协议的播放地址</div>`;
                }
            } else {
                document.getElementById('playerStatus').innerHTML =
                    `<div class="error">获取播放地址失败: ${result.error || '未知错误'}</div>`;
            }
        } catch (error) {
            document.getElementById('playerStatus').innerHTML =
                `<div class="error">获取播放地址时出错: ${error.message}</div>`;
        }
    }

    function setupFlvPlayer(playUrl) {
        const video = document.getElementById('videoPlayer');

        if (flvjs.isSupported()) {
            if (flvPlayer) {
                flvPlayer.destroy();
            }

            // 使用优化配置
            const config = {
                type: 'flv',
                url: playUrl,
                isLive: true,
                hasAudio: false,
                hasVideo: true,
                enableStashBuffer: optimizationConfig.enableStashBuffer,
                stashInitialSize: optimizationConfig.stashInitialSize,
                enableLowLatency: optimizationConfig.enableLowLatency,
                lazyLoad: optimizationConfig.lazyLoad,
                accurateSeek: optimizationConfig.accurateSeek
            };

            console.log('FLV.js配置:', config);
            flvPlayer = flvjs.createPlayer(config);

            flvPlayer.attachMediaElement(video);

            // 添加详细的事件监听
            flvPlayer.on(flvjs.Events.METADATA_ARRIVED, () => {
                console.log('FLV元数据到达');
                document.getElementById('playerStatus').innerHTML = '<div class="success">FLV流元数据加载完成</div>';
            });

            flvPlayer.on(flvjs.Events.STATISTICS_INFO, (info) => {
                updatePlayerStats(info);
            });

            flvPlayer.on(flvjs.Events.LOADING_COMPLETE, () => {
                document.getElementById('playerStatus').innerHTML = '<div class="success">FLV流加载完成</div>';
            });

            flvPlayer.on(flvjs.Events.ERROR, (errorType, detail) => {
                console.error('FLV播放错误:', errorType, detail);
                let errorMsg = `FLV播放错误: ${errorType}`;
                if (detail) errorMsg += ` - ${JSON.stringify(detail)}`;

                document.getElementById('playerStatus').innerHTML =
                    `<div class="error">${errorMsg}</div>`;

                // 网络错误自动重连
                if (optimizationConfig.autoReconnect && errorType === 'NetworkError' && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
                    reconnectAttempts++;
                    setTimeout(() => {
                        if (flvPlayer && isPlayerInitialized) {
                            console.log(`尝试重新连接FLV流 (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...`);
                            document.getElementById('playerStatus').innerHTML =
                                `<div class="result"><span class="loading"></span>重新连接中 (${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})...</div>`;
                            flvPlayer.unload();
                            flvPlayer.load();
                            flvPlayer.play();
                        }
                    }, 3000);
                }
            });

            // 加载并播放
            flvPlayer.load();

            // 使用更智能的播放策略
            const playAttempt = setInterval(() => {
                if (flvPlayer.buffered.length > 0) {
                    flvPlayer.play().catch(e => {
                        console.log('播放等待中:', e.message);
                    });

                    // 如果有足够缓冲，开始播放并清除定时器
                    if (flvPlayer.buffered.end(0) - flvPlayer.currentTime > 0.5) {
                        clearInterval(playAttempt);
                        console.log('FLV播放开始');
                    }
                }
            }, 500);

            // 10秒后无论如何清除定时器
            setTimeout(() => {
                clearInterval(playAttempt);
            }, 10000);

        } else {
            document.getElementById('playerStatus').innerHTML =
                '<div class="error">您的浏览器不支持FLV播放</div>';
        }
    }

    function stopPlayer() {
        const video = document.getElementById('videoPlayer');
        video.pause();
        video.src = '';
        video.style.display = 'none';

        document.getElementById('playerPlaceholder').style.display = 'flex';
        document.getElementById('playerControls').style.display = 'none';
        document.getElementById('playerStats').style.display = 'none';
        document.getElementById('playerStatus').innerHTML = '';

        if (flvPlayer) {
            flvPlayer.pause();
            flvPlayer.unload();
            flvPlayer.detachMediaElement();
            flvPlayer.destroy();
            flvPlayer = null;
        }

        stopPlaybackMonitor();
        isPlayerInitialized = false;
        currentPlayUrl = '';
        reconnectAttempts = 0;
    }

    function togglePlay() {
        const video = document.getElementById('videoPlayer');
        if (video.paused) {
            video.play().catch(e => {
                document.getElementById('playerStatus').innerHTML =
                    `<div class="error">播放失败: ${e.message}</div>`;
            });
        } else {
            video.pause();
        }
    }

    function toggleMute() {
        const video = document.getElementById('videoPlayer');
        video.muted = !video.muted;
    }

    function toggleFullscreen() {
        const video = document.getElementById('videoPlayer');
        if (video.requestFullscreen) {
            video.requestFullscreen();
        } else if (video.webkitRequestFullscreen) {
            video.webkitRequestFullscreen();
        } else if (video.msRequestFullscreen) {
            video.msRequestFullscreen();
        }
    }

    function reconnectPlayer() {
        if (currentPlayUrl) {
            reconnectAttempts = 0;
            document.getElementById('playerStatus').innerHTML =
                '<div class="result"><span class="loading"></span>重新连接FLV流中...</div>';

            stopPlayer();
            setTimeout(() => {
                setupFlvPlayer(currentPlayUrl);
            }, 1000);
        }
    }

    function startPlaybackMonitor() {
        if (playbackMonitor) clearInterval(playbackMonitor);

        playbackMonitor = setInterval(() => {
            const video = document.getElementById('videoPlayer');
            if (flvPlayer && video.readyState === 4 && !video.paused && video.currentTime > 0) {
                const buffered = video.buffered.length > 0 ? video.buffered.end(0) - video.currentTime : 0;

                // 如果缓冲不足，尝试调整播放速度
                if (buffered < 0.5 && !video.paused) {
                    console.warn('缓冲不足，可能需要调整');
                }

                document.getElementById('playerStatus').innerHTML =
                    `<div class="success">FLV播放中 - 缓冲: ${buffered.toFixed(2)}秒</div>`;
            }
        }, 1000);
    }

    function stopPlaybackMonitor() {
        if (playbackMonitor) {
            clearInterval(playbackMonitor);
            playbackMonitor = null;
        }
    }

    function updatePlayerStats(stats) {
        const video = document.getElementById('videoPlayer');
        let statsText = '';

        if (stats) {
            statsText = `速度: ${(stats.speed || 0).toFixed(2)}KB/s | 已加载: ${(stats.loaded || 0).toFixed(2)}KB`;
            if (flvPlayer) {
                const buffered = video.buffered.length > 0 ? video.buffered.end(0) - video.currentTime : 0;
                statsText += ` | 缓冲: ${buffered.toFixed(2)}s`;
            }
        } else {
            const buffered = video.buffered.length > 0 ? video.buffered.end(0) - video.currentTime : 0;
            statsText = `缓冲: ${buffered.toFixed(2)}s | 分辨率: ${video.videoWidth}x${video.videoHeight}`;
        }

        document.getElementById('playerStats').innerText = statsText;
    }

    function showResult(elementId, result, isSuccess) {
        const element = document.getElementById(elementId);
        element.innerHTML = `<div class="result ${isSuccess ? 'success' : 'error'}">
            <strong>${isSuccess ? '成功' : '失败'}</strong><br>
            <pre>${JSON.stringify(result, null, 2)}</pre>
        </div>`;
    }

    function copyToClipboard(text) {
        navigator.clipboard.writeText(text).then(() => {
            alert('已复制到剪贴板');
        }).catch(err => {
            console.error('复制失败:', err);
        });
    }

    // 页面加载时初始化
    window.onload = function() {
        document.getElementById('urlStreamName').value = 'cam01';

        // 为输入框添加回车键支持
        document.getElementById('urlStreamName').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                getUrls();
            }
        });

        document.getElementById('token').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                setupPlayer();
            }
        });
    };

    // 添加键盘快捷键支持
    document.addEventListener('keydown', function(e) {
        // 空格键控制播放/暂停
        if (e.key === ' ' && isPlayerInitialized) {
            e.preventDefault();
            togglePlay();
        }

        // M键控制静音
        if (e.key === 'm' && isPlayerInitialized) {
            e.preventDefault();
            toggleMute();
        }

        // F键控制全屏
        if (e.key === 'f' && isPlayerInitialized) {
            e.preventDefault();
            toggleFullscreen();
        }

        // R键重新连接
        if (e.key === 'r' && isPlayerInitialized) {
            e.preventDefault();
            reconnectPlayer();
        }

        // ESC键退出全屏
        if (e.key === 'Escape') {
            if (document.fullscreenElement) {
                document.exitFullscreen();
            }
        }
    });

    // 添加视频播放状态监听
    const video = document.getElementById('videoPlayer');
    video.addEventListener('play', function() {
        console.log('FLV视频开始播放');
        document.getElementById('playerStatus').innerHTML = '<div class="success">FLV播放中</div>';
    });

    video.addEventListener('pause', function() {
        console.log('FLV视频暂停');
        document.getElementById('playerStatus').innerHTML = '<div class="result">FLV已暂停</div>';
    });

    video.addEventListener('ended', function() {
        console.log('FLV视频播放结束');
        document.getElementById('playerStatus').innerHTML = '<div class="result">FLV播放结束</div>';
    });

    video.addEventListener('error', function(e) {
        console.error('FLV视频播放错误:', e);
        document.getElementById('playerStatus').innerHTML =
            '<div class="error">FLV视频播放发生错误，请检查网络连接和播放地址</div>';
    });

    video.addEventListener('waiting', function() {
        document.getElementById('playerStatus').innerHTML =
            '<div class="result"><span class="loading"></span>FLV缓冲中...</div>';
    });

    video.addEventListener('canplay', function() {
        if (!video.paused) {
            document.getElementById('playerStatus').innerHTML = '<div class="success">FLV播放中</div>';
        }
    });

    // 添加页面可见性变化监听
    document.addEventListener('visibilitychange', function() {
        if (document.hidden && isPlayerInitialized) {
            // 页面隐藏时暂停播放以节省资源
            video.pause();
        }
    });

    // 添加网络状态变化监听
    window.addEventListener('online', function() {
        console.log('网络连接恢复');
        document.getElementById('playerStatus').innerHTML = '<div class="success">网络已恢复</div>';
        if (isPlayerInitialized && video.paused) {
            setTimeout(() => {
                video.play().catch(e => {
                    console.log('自动播放失败，需要用户交互');
                });
            }, 1000);
        }
    });

    window.addEventListener('offline', function() {
        console.log('网络连接断开');
        document.getElementById('playerStatus').innerHTML =
            '<div class="error">网络连接已断开，请检查网络设置</div>';
    });

    // 添加窗口关闭前的清理
    window.addEventListener('beforeunload', function() {
        stopPlayer();
    });
</script>
</body>
</html>